package com.yby6.system.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.util.CollectionUtils;
import com.yby6.common.constant.Constants;
import com.yby6.common.constant.MenuConstants;
import com.yby6.common.core.AjaxResult;
import com.yby6.common.utils.StringUtils;
import com.yby6.common.core.domain.SysUser;
import com.yby6.system.mapper.SysMenuMapper;
import com.yby6.common.core.domain.SysMenu;
import com.yby6.system.service.SysMenuService;
import com.yby6.system.vo.MenuTreeVo;
import com.yby6.system.vo.MetaVo;
import com.yby6.system.vo.RouterVo;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Description: 杨不易网站 :www.yangbuyi.top
 * date: 2021/5/21
 *
 * @author Yang Shuai
 * @since JDK 1.8
 **/

@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

	@Resource
	private SysMenuMapper sysMenuMapper;

	/**
	 * 根据用户ID查询系统菜单
	 *
	 * @param sysMenu
	 * @return
	 */
	@Override
	public List<SysMenu> selectMenuList(SysMenu sysMenu, SysUser sysUser) {
		List<SysMenu> menuList = null;
		// 管理员显示所有菜单信息
		if (Constants.USER_ADMIN.equals(sysUser.getIsAdmin())) {
			menuList = sysMenuMapper.selectMenuList(sysMenu);
		} else {
			// 根据用户ID查询对应的菜单列表
			menuList = sysMenuMapper.selectMenuListByUserId(sysMenu, sysUser.getId());
		}
		return menuList;
	}

	/**
	 * 根据ID查询菜单信息
	 *
	 * @param menuId
	 * @return
	 */
	@Override
	public SysMenu selectMenuById(Long menuId) {
		return this.sysMenuMapper.selectMenuById(menuId);
	}


	/**
	 * 根据用户ID查询拥有的权限标识
	 *
	 * @param userId
	 * @return
	 */
	@Override
	public List<String> selectMenuPermsByUserId(Long userId) {
		return sysMenuMapper.selectMenuPermsByUserId(userId);
	}

	/**
	 * 构造树形菜单列表
	 *
	 * @param sysMenu
	 * @param currentUser
	 * @return
	 */
	@Override
	public List<MenuTreeVo> getTreeList(SysMenu sysMenu, SysUser currentUser) {
		// 获取处理的菜单集合
		List<SysMenu> menuList = Collections.EMPTY_LIST;
		// 1. 前端角色管理传递特殊字符判断是否有参数
		if (StringUtils.isNotBlank((String) sysMenu.getParams().get("role"))) {
			// 则需要全部的下拉菜单数据
			menuList = this.sysMenuMapper.selectMenuList(sysMenu);
		} else {
			// 否则根据当前用户查询对应的下拉菜单数据（列表之类的）
			menuList = this.selectMenuList(sysMenu, currentUser);
		}
		// 根据模糊查询获取相同的父ID
		SysMenu menuVo = this.sysMenuMapper.selectMenuByAnonymity(sysMenu);
		if (null == menuVo) {
			menuVo = new SysMenu();
			menuVo.setParentId(0L);
		}
		// 记录动态父节点ID
		SysMenu finalMenuVo = menuVo;
		// 过滤所有的顶部菜单
		List<SysMenu> finalMenuList = menuList;
		List<MenuTreeVo> collect = menuList.stream()
				// 只要父菜单是0那就是顶部菜单
				.filter(e -> e.getParentId().equals(finalMenuVo.getParentId()))
				// 将顶部菜单转换成我们的视图类
				.map(e -> {
					MenuTreeVo menuTreeVo = new MenuTreeVo();
					BeanUtils.copyProperties(e, menuTreeVo);
					return menuTreeVo;
				})
				// 根据顶部菜单ID 递归从剩余的列表当中找子节点
				.map(e -> {
					e.setChildren(buildMenuChildren(e, finalMenuList));
					if (CollectionUtils.isEmpty(e.getChildren())) {
						e.setChildren(null);
					}
					return e;
				}).collect(Collectors.toList());
		return collect;
	}

	/**
	 * 查询出当前登录用户所拥有的启用中的所有菜单
	 *
	 * @param user
	 * @return
	 */
	private List<SysMenu> selectMenuTreeByUserId(SysUser user) {
		List<SysMenu> menus = null;
		if (Constants.USER_ADMIN.equals(user.getIsAdmin())) {
			menus = sysMenuMapper.selectMenuTreeAll();
		} else {
			menus = sysMenuMapper.selectMenuTreeByUserId(user.getId());
		}
		return menus;
	}

	/**
	 * 根据当前登陆用户ID获取动态路由菜单
	 *
	 * @param loginUser
	 * @return 动态路由
	 */
	@Override
	public List<RouterVo> getRouters(SysUser loginUser) {
		// 1. 查询出当前登录用户所拥有的启用中的所有菜单（权限不要查）
		List<SysMenu> sysMenuList = selectMenuTreeByUserId(loginUser);
		// 2. 构造成树形结构，也就是 `SysMenuVo`
		List<MenuTreeVo> collect = sysMenuList.stream()
				// 过滤全部父节点
				.filter(e -> e.getParentId().equals(MenuConstants.DEFAULT_PARENT_ID))
				// 构造树形结构
				.map(e -> {
					MenuTreeVo menuTreeVo = new MenuTreeVo();
					BeanUtils.copyProperties(e, menuTreeVo);
					return menuTreeVo;
				})
				// 递归判断该父节点下是否有子节点
				.map(e -> {
					if (CollectionUtils.isEmpty(e.getChildren())) {
						// 设置为空集合
						e.setChildren(new ArrayList<>(0));
					}
					e.setChildren(buildMenuChildren(e, sysMenuList));
					return e;
				}).collect(Collectors.toList());
		// 3. 构造成路由树
		return buildMenus(collect);
	}

	/**
	 * 构建前端动态路由所需要的菜单
	 *
	 * @param menuTreeVos 菜单列表
	 * @return 路由列表
	 */
	private List<RouterVo> buildMenus(List<MenuTreeVo> menuTreeVos) {
		// 1. 遍历上面的菜单树
		List<RouterVo> routerVoList = new ArrayList<>();
		for (MenuTreeVo menuTreeVo : menuTreeVos) {
			// 2. 创建 `RouterVo` 对象，将菜单数据转换成路由视图对象
			RouterVo routerVo = new RouterVo();
			routerVo.setHidden(MenuConstants.HIDDEN_STATE.equals(menuTreeVo.getVisible()));
			// 0. 构造菜单名称,是否外链名称
			routerVo.setName(getRouteName(menuTreeVo));
			// 1. 构造跳转路径，用多个 '/' 拼接
			routerVo.setPath(getRouterPath(menuTreeVo));
			// 2. 构造组件路径，用多个 '/' 拼接
			routerVo.setComponent(getComponent(menuTreeVo));
			// 3. 构造meta数据
			routerVo.setMeta(new MetaVo(menuTreeVo.getMenuName(), menuTreeVo.getIcon(), MenuConstants.YES_CACHE.equals(menuTreeVo.getIsCache())));
			// 3. 如果当前是目录，并且子菜单不为空，就递归构造子菜单
			if (menuTreeVo.getMenuType().equals(MenuConstants.TYPE_DIR) && !CollectionUtils.isEmpty(menuTreeVo.getChildren()) &&  menuTreeVo.getChildren().size() > 0) {
				// 为根节点菜单
				routerVo.setAlwaysShow(true);
				// 不进行重定向
				routerVo.setRedirect("noRedirect");
				// 递归子节点
				routerVo.setChildren(buildMenus(menuTreeVo.getChildren()));
			}
			// 4.是否为外链跳转
			if (isMenuFrame(menuTreeVo)) {
				routerVo.setMeta(null);
				List<RouterVo> childrenList = new ArrayList<RouterVo>();
				RouterVo children = new RouterVo();
				children.setPath(menuTreeVo.getRouterPath());
				children.setComponent(menuTreeVo.getComponentUrl());
				children.setName(StringUtils.capitalize(menuTreeVo.getRouterPath()));
				children.setMeta(new MetaVo(menuTreeVo.getMenuName(), menuTreeVo.getIcon(), MenuConstants.YES_CACHE.equals(menuTreeVo.getIsCache())));
				childrenList.add(children);
				routerVo.setChildren(childrenList);
			}
			routerVoList.add(routerVo);
		}
		return routerVoList;
	}


	/**
	 * 递归构造树形菜单列表
	 *
	 * @param menuTreeVo 父节点对象
	 * @param menuList   菜单列表
	 * @return 树形结构菜单
	 */
	private List<MenuTreeVo> buildMenuChildren(MenuTreeVo menuTreeVo, List<SysMenu> menuList) {
		// 过滤判断是否拥有子菜单
		List<MenuTreeVo> menuTreeVoList = menuList.stream().filter(e -> e.getParentId().equals(menuTreeVo.getMenuId()))
				.map(e -> {
					// 进行将子菜单注入到父节点children集合
					MenuTreeVo children = new MenuTreeVo();
					BeanUtils.copyProperties(e, children);
					return children;
				}).map(e -> {
					// 递归找到本次获取到的子菜单的子菜单(无限层级菜单)
					e.setChildren(buildMenuChildren(e, menuList));
					if (CollectionUtils.isEmpty(e.getChildren())) {
						e.setChildren(null);
					}
					return e;
				}).collect(Collectors.toList());
		return CollectionUtils.isEmpty(menuTreeVoList) ? null : menuTreeVoList;
	}


	/**
	 * 是否为菜单内部跳转
	 *
	 * @param menu 菜单信息
	 * @return 结果
	 */
	public boolean isMenuFrame(MenuTreeVo menu) {
		return menu.getParentId().intValue() == 0 && MenuConstants.TYPE_MENU.equals(menu.getMenuType())
				&& menu.getIsFrame().equals(MenuConstants.NO_FRAME);
	}

	/**
	 * 获取路由名称
	 *
	 * @param menu 菜单信息
	 * @return 路由名称
	 */
	public String getRouteName(MenuTreeVo menu) {
		String routerName = StringUtils.capitalize(menu.getRouterPath());
		// 非外链并且是一级目录（类型为目录）
		if (isMenuFrame(menu)) {
			routerName = StringUtils.EMPTY;
		}
		return routerName;
	}

	/**
	 * 获取路由地址
	 *
	 * @param menu 菜单信息
	 * @return 路由地址
	 */
	public String getRouterPath(MenuTreeVo menu) {
		String routerPath = menu.getRouterPath();
		// 非外链并且是一级目录（类型为目录）
		if (0 == menu.getParentId().intValue() && MenuConstants.TYPE_DIR.equals(menu.getMenuType())
				&& MenuConstants.NO_FRAME.equals(menu.getIsFrame())) {
			routerPath = "/" + menu.getRouterPath();
		}
		// 非外链并且是一级目录（类型为菜单）
		else if (isMenuFrame(menu)) {
			routerPath = "/";
		}
		// 否则返回外链跳转
		return routerPath;
	}

	/**
	 * 获取组件信息
	 *
	 * @param menu 菜单信息
	 * @return 组件信息
	 */
	public String getComponent(MenuTreeVo menu) {
		String component = MenuConstants.LAYOUT;
		if (StringUtils.isNotEmpty(menu.getComponentUrl()) && !isMenuFrame(menu)) {
			component = menu.getComponentUrl();
		} else if (StringUtils.isEmpty(menu.getComponentUrl()) && isParentView(menu)) {
			component = MenuConstants.PARENT_VIEW;
		}
		return component;
	}

	/**
	 * 是否为parent_view组件
	 *
	 * @param menu 菜单信息
	 * @return 结果
	 */
	public boolean isParentView(MenuTreeVo menu) {
		return menu.getParentId().intValue() != 0 && MenuConstants.TYPE_DIR.equals(menu.getMenuType());
	}


	/**
	 * 添加菜单
	 *
	 * @param sysMenu
	 * @return
	 */
	@Override
	public AjaxResult addMenu(SysMenu sysMenu) {
		// 校验菜单名称是否唯一
		SysMenu menu = sysMenuMapper.checkMenuNameUnique(sysMenu);
		if (menu != null) {
			return AjaxResult.error("新增菜单'" + sysMenu.getMenuName() + "'失败，菜单名称已存在");
		} else if (MenuConstants.YES_FRAME.equals(sysMenu.getIsFrame())
				&& !StringUtils.startsWithAny(sysMenu.getRouterPath(), Constants.HTTP, Constants.HTTPS)) {
			return AjaxResult.error("新增菜单'" + sysMenu.getMenuName() + "'失败，地址必须以http(s)://开头");
		}
		return sysMenuMapper.insertMenu(sysMenu) > 0 ? AjaxResult.success() : AjaxResult.error();
	}

	/**
	 * 修改菜单
	 *
	 * @param sysMenu
	 * @return
	 */
	@Override
	public AjaxResult updateMenu(SysMenu sysMenu) {
		// 校验菜单名称是否唯一
		SysMenu menu = sysMenuMapper.checkMenuNameUnique(sysMenu);
		if (menu != null && !sysMenu.getMenuId().equals(menu.getMenuId())) {
			return AjaxResult.error("修改菜单'" + sysMenu.getMenuName() + "'失败，菜单名称已存在");
		} else if (MenuConstants.YES_FRAME.equals(sysMenu.getIsFrame())
				&& !StringUtils.startsWithAny(sysMenu.getRouterPath(), Constants.HTTP, Constants.HTTPS)) {
			return AjaxResult.error("修改菜单'" + sysMenu.getMenuName() + "'失败，地址必须以http(s)://开头");
		} else if (sysMenu.getMenuId().equals(sysMenu.getParentId())) {
			return AjaxResult.error("修改菜单'" + sysMenu.getMenuName() + "'失败，上级菜单不能选择自己");
		}
		return sysMenuMapper.updateMenu(sysMenu) > 0 ? AjaxResult.success() : AjaxResult.error();
	}

	/**
	 * 校验是否存在子菜单
	 *
	 * @param menuId 菜单ID
	 * @return 结果 true 存在 false 不存在
	 */
	@Override
	public boolean hasChildByMenuId(Long menuId) {
		int result = this.sysMenuMapper.hasChildByMenuId(menuId);
		return result > 0 ? true : false;
	}

	/**
	 * 校验菜单是否分配
	 *
	 * @param menuId 菜单id
	 * @return 结果 true 存在 false 不存在
	 */
	@Override
	public boolean checkMenuExistRole(Long menuId) {
		int result = this.sysMenuMapper.checkMenuExistRole(menuId);
		return result > 0 ? true : false;
	}

	/**
	 * 删除菜单
	 *
	 * @param menuId 菜单ID
	 * @return 结果 ture 成功 false 失败
	 */
	@Override
	public int deleteMenuById(Long menuId) {
		return sysMenuMapper.deleteMenuById(menuId);
	}

}
